package myJs.basic344

import myJs.Implicits._
import myJs.myPkg.jquery._
import myJs.myPkg._
import myJs.tool.Tool
import org.scalajs.dom.ext.Ajax
import org.scalajs.dom.{Element, Event, document}
import scalatags.Text.all._

import scala.collection.immutable.SeqMap
import scala.concurrent.ExecutionContext.Implicits.global
import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.JSConverters._
import scala.scalajs.js.JSON
import scala.scalajs.js.annotation._

@JSExportTopLevel("Basic344Search")
object Basic344Search {

  val geneIdId = "geneId"
  val geneNameId = "geneName"
  val regionId = "region"
  val keywordId = "keyword"
  val formId = "form"

  def geneIdFormJq = $(s"#${geneIdId} #${formId}")

  def geneNameFormJq = $(s"#${geneNameId} #${formId}")

  def regionFormJq = $(s"#${regionId} #${formId}")

  def keywordFormJq = $(s"#${keywordId} #${formId}")

  @JSExport("init")
  def init = {
    refreshSpecies
    refreshTypeahead
    bootStrapValidator
    registerEvent
    val geneId = $("#myGeneId").`val`().toString.trim
    $(".myMainUl a[data-toggle='tab']").on("shown.bs.tab", (y: Element, e: Event) => {
      $("#result").hide()
    })
  }

  def refreshTypeahead = {
    refreshGeneIdTypeahead
    refreshChrTypeahead
  }

  def extractor(query: String) = {
    val result = js.RegExp("([^,]+)$").exec(query)
    if (result != null && result(1).isDefined) {
      result(1).toString.trim
    } else ""
  }

  val highlighterF = (y: Typeahead, item: String) => {
    val input = y.query
    val query = extractor(input).replaceAll("[\\-\\[\\]{}()*+?.,\\\\^$|#\\s]", "\\$&")
    val r = (s"(?i)" + query).r
    r.replaceAllIn(item, matcher =>
      s"<strong>${matcher.group(0)}</strong>"
    )
  }

  val matcher = (y: Typeahead, item: String) => {
    val tquery = extractor(y.query)
    if (tquery.isBlank) {
      false
    } else {
      ~(item.toLowerCase.indexOf(tquery.toLowerCase))
    }
  }

  val updater = (y: Typeahead, item: String) => {
    s"${y.$element.`val`().toString.replaceAll("[^,]*$", "")}${item},"
  }

  def refreshGeneIdTypeahead = {
    val url = g.jsRoutes.controllers.Basic344Controller.allGeneIds().url.toString
    Ajax.get(url = s"${url}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJArS
      geneIdFormJq.findInputByName(s"geneId").typeahead("destroy").
        typeahead(TypeaheadOptions.source(rs).highlighter(highlighterF).
          updater(updater).matcher(matcher)
        )
    }
  }

  def refreshChrTypeahead = {
    val formJq = regionFormJq
    val species = formJq.find(s":input[name='species']:checked").`val`().toString
    val url = g.jsRoutes.controllers.Basic344Controller.allChrs().url.toString
    Ajax.get(url = s"${url}?species=${species}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJArS
      formJq.findInputByName(s"chr").typeahead("destroy").
        typeahead(TypeaheadOptions.source(rs)
        )
    }
  }

  def registerEvent = {
    $(document.body).on("click", s".myGeneIdSearch", "", (y: Element) => {
      geneIdSearch
    })
    $(document.body).on("click", s".myShowExample", "", (y: Element) => {
      showExample(y)
    })
    $(document.body).on("click", s".myRegionSearch", "", (y: Element) => {
      regionSearch
    })
  }

  def showExample(y: Element) = {
    val inputJq = $(y).parents(".form-group").find(":input")
    val str = $(y).text().trim
    inputJq.`val`(str)
    inputJq.parents(s"#${formId}").bv.revalidateField(inputJq)
  }

  def refreshSpecies = {
    refreshGeneIdSpecies
    refreshGeneNameSpecies
    refreshRegionSpecies
  }

  def refreshGeneIdSpecies = {
    val speciesMap = Tool.speciesMap
    val html = speciesMap.zipWithIndex.map { case ((k,v), i) =>
      label(
        marginRight := 15,
        width := 200,
      )(input(
        `type` := "radio",
        name := "species",
        value := k,
        if (i == 0) checked else raw(""),
        onchange := s"Search.geneIdExampleChange(this)"
      )(v)
      )
    }.mkString
    geneIdFormJq.find(s"#species").empty().html(html)
  }

  def refreshGeneNameSpecies = {
    val speciesMap = Tool.speciesMap
    val html = speciesMap.zipWithIndex.map { case ((k,v), i) =>
      label(
        marginRight := 15,
        width := 200,
      )(input(
        `type` := "radio",
        name := "species",
        value := k,
        if (i == 0) checked else raw(""),
        onchange := s"Search.geneNameExampleChange(this)"
      )(v)
      )
    }.mkString
    geneNameFormJq.find(s"#species").empty().html(html)
  }

  def refreshRegionSpecies = {
    val speciesMap = Tool.speciesMap
    val html = speciesMap.zipWithIndex.map { case ((k,v), i) =>
      label(
        marginRight := 15,
        width := 200,
      )(input(
        `type` := "radio",
        name := "species",
        value := k,
        if (i == 0) checked else raw(""),
        onchange := s"Search.regionExampleChange(this)"
      )(v)
      )
    }.mkString
    regionFormJq.find(s"#species").empty().html(html)
  }

  def myFmt(columnName: String): js.Function = (v: js.Any, row: js.Dictionary[js.Any]) => columnName match {
    case "id" =>
      val url = g.jsRoutes.controllers.Basic344Controller.detailInfo().url
      val hf = s"${url}?geneId=${v}"
      a(
        target := "_blank",
        href := hf
      )(v.toString).render
    case "chr" =>
      row("chr") + ":" + row("start") + "-" + row("end")
    case _ => v
  }

  def geneIdSearch = {
    val formJq = geneIdFormJq
    val bv = formJq.bv
    bv.validate()
    val valid = bv.isValid()
    if (valid) {
      val data = formJq.serialize()
      val index = layer.alert(Tool.element, Tool.layerOptions)
      val url = g.jsRoutes.controllers.Basic344Controller.searchByGeneId().url.toString
      Ajax.post(url = s"${url}", data = data, headers = Map("Content-Type" -> "application/x-www-form-urlencoded"),
        responseType = "application/json").map { xhr =>
        layer.close(index)
        val columns = myColumns
        val array = xhr.responseText.toJArJDJAn
        val options = TableOptions.data(array).columns(columns)
        $("#table").bootstrapTable("destroy").bootstrapTable(options)
        $("#result").show()
      }
    }

  }

  def myColumns = {
    val map = SeqMap("id" -> "ID","locus" -> "Locus", "strand" -> "Strand")
    map.map { case (fieldName, thName) =>
      val fmt = myFmt(fieldName)
      ColumnOptions.field(fieldName).title(thName).sortable(true).titleTooltip(fieldName).formatter(fmt)
    }.toJSArray
  }

  def regionSearch = {
    val formJq = regionFormJq
    val bv = formJq.bv
    bv.validate()
    val valid = bv.isValid()
    if (valid) {
      val data = formJq.serialize()
      val index = layer.alert(Tool.element, Tool.layerOptions)
      val url = g.jsRoutes.controllers.Basic344Controller.searchByRegion().url.toString
      Ajax.post(url = s"${url}", data = data, headers = Map("Content-Type" -> "application/x-www-form-urlencoded"),
        responseType = "application/json").map { xhr =>
        layer.close(index)
        val columns = myColumns
        val array = xhr.responseText.toJArJDJAn
        val options = TableOptions.data(array).columns(columns)
        $("#table").bootstrapTable("destroy").bootstrapTable(options)
        $("#result").show()
      }
    }

  }

  def bootStrapValidator = {
    geneIdBootStrapValidator
    geneNameBootStrapValidator
    regionBootStrapValidator
    keywordBootStrapValidator
  }

  def geneIdBootStrapValidator = {
    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "geneId" -> {
          val info = "Gene Id"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required！"
              ),
            )
          )
        },
      )
    )
    $(s"#${geneIdId} #${formId}").bootstrapValidator(dict)
  }

  def geneNameBootStrapValidator = {
    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "geneName" -> {
          val info = "Gene Name"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required！"
              ),
            )
          )
        },
      )
    )
    geneNameFormJq.bootstrapValidator(dict)
  }

  def regionBootStrapValidator = {
    val formJq = regionFormJq
    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "chr" -> {
          val info = "Chromosome"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required！"
              ),
            )
          )
        },
        "start" -> {
          val info = "Start"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required！"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "end" -> {
          val info = "End"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required！"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
      )
    )
    formJq.bootstrapValidator(dict)
  }

  def keywordBootStrapValidator = {
    val formJq = keywordFormJq
    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "keyword" -> {
          val info = "Keyword"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required！"
              ),
            )
          )
        },
      )
    )
    formJq.bootstrapValidator(dict)
  }


}
